/*
 *                    COPYRIGHT NOTICE
 *
 *           This code is copyright protected by Freedom Inc.
 *
 *              2001-2012 All Rights Are Reserved
 */
package com.freedom.oldsong.util;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

import org.apache.log4j.Logger;

/**
 * This utility class is for date operations, including all kinds of
 * conversions.
 * 
 * @author steven.wang
 *
 */
public final class DateUtils {
	
	private static final Logger logger=Logger.getLogger(DateUtils.class);

	public static final String BLEUM_DATE_FORMAT = "MM/dd/yyyy";

	public static final String BLEUM_DATE_TIME_FORMAT = "MM/dd/yyyy hh:mm";

	public static final String BLEUM_DATE_TIME_FORMAT_24H = "MM/dd/yyyy HH:mm";

	public static final String BLEUM_TIME_24 = "HH:mm:00";

	private DateUtils(){
		
	}
	
	public static final Date getWorkingFinishedDay(Date start, int interval) {
		if (start == null) {
			throw new IllegalArgumentException("start date can't be null.");
		}
		if (interval < 0) {
			throw new IllegalArgumentException("duration must be larger than 0.");
		}
		if (interval == 0) {
			return start;
		}
		
		int offdays = 0;
		Calendar cal = Calendar.getInstance();
		cal.setTime(start);
		cal.set(Calendar.DATE, cal.get(Calendar.DATE) + interval);
		Date end = cal.getTime();
		for (Date tmp = start; tmp.compareTo(end) < 0;) {
			cal.setTime(tmp);
			cal.set(Calendar.DATE, cal.get(Calendar.DATE) + 1);
			if (cal.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY
					|| cal.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY) {
				offdays++;
			}
			tmp = cal.getTime();
		}
		return getWorkingFinishedDay(end, offdays);
	}

	public static final Date getWorkingStartedDay(Date finsih, int interval) {
		if (finsih == null) {
			throw new IllegalArgumentException("start date can't be null.");
		}
		if (interval < 0) {
			throw new IllegalArgumentException("duration must be larger than 0.");
		}
		if (interval == 0) {
			return finsih;
		}
		int offdays = 0;
		Calendar cal = Calendar.getInstance();
		cal.setTime(finsih);
		cal.set(Calendar.DATE, cal.get(Calendar.DATE) - interval);
		Date start = cal.getTime();
		for (Date tmp = finsih; tmp.compareTo(start) > 0;) {
			cal.setTime(tmp);
			cal.set(Calendar.DATE, cal.get(Calendar.DATE) - 1);
			if (cal.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY
					|| cal.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY) {
				offdays++;
			}
			tmp = cal.getTime();
		}
		return getWorkingStartedDay(start, offdays);
	}

	public static final Date getAdjustedWorkingdDay(Date date, int interval) {
		if (interval == 0) {
			return date;
		}
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		if (interval > 0) {
			while (interval != getWorkingDuration(date, cal.getTime()).intValue()) {
				cal.set(Calendar.DATE, cal.get(Calendar.DATE) + 1);
			}
			return cal.getTime();
		} else {
			while (-interval != getWorkingDuration(cal.getTime(), date).intValue()) {
				cal.set(Calendar.DATE, cal.get(Calendar.DATE) - 1);
			}
			return cal.getTime();
		}
	}

	public static final Date convertStringToDate(String dateStr) {
		return convertStringToDate(dateStr, BLEUM_DATE_FORMAT);
	}

	public static long getDuration(Date beginDate, Date endDate) {
		Date end = endDate;
		if (end == null){
			end = new Date();
		}
		if (beginDate == null){
			return -1;
		}
		long beginDay = beginDate.getTime();
		long endDay = end.getTime();
		return (endDay - beginDay) / 1000 / 3600 / 24;
	}

	public static final Date convertStringToDate(String dateStr, String dateFormat) {
		if (dateStr == null || "".equals(dateStr.trim())){
			return null;
		}
		SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
		Date date=null;
		try {
			date = sdf.parse(dateStr.trim());
		} catch (ParseException e) {
			logger.error(e.getMessage());
		}
		return date;
	}

	public static final long convertDateToLong(Date date) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);

		int year = cal.get(Calendar.YEAR);
		int month = cal.get(Calendar.MONTH) + 1;
		int day = cal.get(Calendar.DATE);

		long l = year * 10000l + month * 100l + day;
		// return new Long(l);
		return l;
	}

	/**
	 * 
	 * @param date
	 *            specific date to be a reference
	 * @param field
	 *            specific day of the week, Calendar.SUNDAY to Calendar.SATURDAY
	 * @param adjust
	 *            adjusted week (0: current week, -1: last week, 1 next week;
	 *            etc)
	 * @return
	 */
	public static final Date findDayOfWeek(Date date, int field, int adjust) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
		if (dayOfWeek == field) {
			return date;
		}
		cal.set(Calendar.DAY_OF_YEAR, cal.get(Calendar.DAY_OF_YEAR) - dayOfWeek + field + adjust
				* 7);
		return cal.getTime();
	}
	
	public static final Date findWeekDay(int adjust, int weekday) {
		return findDayOfWeek(Calendar.getInstance().getTime(), weekday, adjust);
	}
	
	public static final Date findSunday(int adjust) {
		return findDayOfWeek(Calendar.getInstance().getTime(), Calendar.SUNDAY, adjust);
	}

	public static final Date findSaturday(int adjust) {
		return findDayOfWeek(Calendar.getInstance().getTime(), Calendar.SATURDAY, adjust);
	}

	/**
	 * compare whether two dates are in the same week or not
	 * 
	 * @param firstDate
	 * @param secondDate
	 * @return 0 if firstDate and secondDate is in the same week -1 if
	 *         firstDate's week is before secondDate's week 1 if firstDate's
	 *         week is after secondDate's week
	 */
	public static int compareWeek(Date firstDate, Date secondDate) {
		return roundDate(findDayOfWeek(firstDate, Calendar.SUNDAY, 0)).compareTo(
				roundDate(findDayOfWeek(secondDate, Calendar.SUNDAY, 0)));
	}

	public static Date roundDate(Date date) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		cal.set(Calendar.HOUR_OF_DAY, 0);
		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);
		return cal.getTime();
	}

	public static final Date findPreSaturday(Date date) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);

		int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);

		int dayOfYear = cal.get(Calendar.DAY_OF_YEAR);
		cal.set(Calendar.DAY_OF_YEAR, dayOfYear - dayOfWeek);

		return cal.getTime();
	}

	public static final Date findPreFriday(Date date, int period) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);

		int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);

		int dayOfYear = cal.get(Calendar.DAY_OF_YEAR);
		cal.set(Calendar.DAY_OF_YEAR, dayOfYear - dayOfWeek -1 -7*(period-1));

		return cal.getTime();
	}
	
	public static final Date findPrePreSunday(Date date) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);

		int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);

		int dayOfYear = cal.get(Calendar.DAY_OF_YEAR);
		cal.set(Calendar.DAY_OF_YEAR, dayOfYear - dayOfWeek - 6);

		return cal.getTime();
	}

	public static final Date findPreSunday(Date date) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);

		int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
		if (dayOfWeek == Calendar.SUNDAY) {
			return date;
		}
		int dayOfYear = cal.get(Calendar.DAY_OF_YEAR);
		cal.set(Calendar.DAY_OF_YEAR, dayOfYear - dayOfWeek + 1);

		return cal.getTime();
	}

	public static final Date findAfterSaturday(Date date) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);

		int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
		if (dayOfWeek == Calendar.SATURDAY) {
			return date;
		}
		int dayOfYear = cal.get(Calendar.DAY_OF_YEAR);
		cal.set(Calendar.DAY_OF_YEAR, dayOfYear + (7 - dayOfWeek));

		return cal.getTime();
	}

	public static final Date convertLongToDate(long l) {
		Long year = l / 10000;
		Long month = (l % 10000) / 100;
		Long day = l % 100;

		String dateStr = month.toString() + "/" + day.toString() + "/" + year.toString();
		return DateUtils.convertStringToDate(dateStr);
	}

	public static final boolean isWeekend(Date date) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		int day = cal.get(Calendar.DAY_OF_WEEK);
		return (day == Calendar.SATURDAY) || (day == Calendar.SUNDAY);
	}

	public static final String getWeekDay(Date date) {
		String[] weekDays = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
				"Saturday" };
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);

		int day = cal.get(Calendar.DAY_OF_WEEK);
		int index = (day - 1);
		if (index < 0){
			return weekDays[0];
		}
		else if (index > 6){
			return weekDays[6];
		}
		return weekDays[index];
	}

	public static final String getCurrentWeekDay() {
		return getWeekDay(new Date());
	}

	/**
	 * date formater.
	 * 
	 * @param date
	 *            the date.
	 * @param pattern
	 *            the format pattern.
	 * @return date string formatted.
	 */
	public static String formatDate(final Date date, final String pattern) {

		String fmt = pattern;

		if (date == null) {
			return "";
		}
		if (pattern == null) {
			fmt = BLEUM_DATE_FORMAT;
		}

		DateFormat fmter = new SimpleDateFormat(fmt);
		return fmter.format(date);

	}

	/**
	 * date formater.
	 * 
	 * @param date
	 *            the date.
	 * @return date string formatted.
	 */
	public static String formatDate(final Date date) {
		return formatDate(date, BLEUM_DATE_FORMAT);
	}

	/***************************************************************************
	 * 
	 * @param date
	 * @return date string e.g. 23:14:00
	 */
	public static String formatTime(final Date date) {
		return formatDate(date, BLEUM_TIME_24);
	}

	public static String getCurrentDate() {
		return formatDate(new Date());
	}

	public static String getCurrentDateTime() {
		return format24HDateTime(new Date());
	}

	public static String formatDateTime(final Date date) {
		return formatDate(date, BLEUM_DATE_TIME_FORMAT);
	}

	public static String format24HDateTime(final Date date) {
		return formatDate(date, BLEUM_DATE_TIME_FORMAT_24H);
	}

	public static Date parseDate(String date){
		SimpleDateFormat sd = new SimpleDateFormat(BLEUM_DATE_FORMAT);
		try {
			return sd.parse(date);
		} catch (ParseException e) {
			logger.error(e.getMessage());
			return null;
		}
	}

	public static Date parse24DateTime(String dateTime) {
		SimpleDateFormat sd = new SimpleDateFormat(BLEUM_DATE_TIME_FORMAT_24H);
		try {
			return sd.parse(dateTime);
		} catch (Exception e) {
			logger.error(e.getMessage());
			return null;
		}
	}

	public static Date parseDateTime(String dateTime) {
		SimpleDateFormat sd = new SimpleDateFormat(BLEUM_DATE_TIME_FORMAT);
		try {
			return sd.parse(dateTime);
		} catch (Exception e) {
			logger.error(e.getMessage());
			return null;
		}
	}

	public static String getCurrentMonth() {
		String[] months = { "January", "February", "March", "April", "May", "June", "July",
				"August", "September", "October", "November", "December" };
		Calendar cal = Calendar.getInstance();
		cal.setTime(new Date());
		int index = cal.get(Calendar.MONTH);
		if (index < 0) {
			return months[0];
		} else if (index > 11) {
			return months[11];
		}
		return months[index];
	}

	public static int getCurrentMonthNum() {
		Calendar cal = Calendar.getInstance();
		cal.setTime(new Date());
		return cal.get(Calendar.MONTH) + 1;
	}

	public static int getCurrentYear() {
		Calendar cal = Calendar.getInstance();
		cal.setTime(new Date());
		return cal.get(Calendar.YEAR);
	}

	public static Date getDayByAmount(int amount) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(new Date());
		cal.add(Calendar.DATE, amount);
		return cal.getTime();
	}

	public static String findDayByMonth(Date date, int index) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		cal.add(Calendar.DAY_OF_YEAR, index);
		int day = cal.get(Calendar.DAY_OF_MONTH);
		if (day >= 10) {
			return String.valueOf(day);
		}
		return "0" + String.valueOf(day);
	}

	public static Date getBleumDateFormatDate() {
		Calendar cal = Calendar.getInstance();
		cal.setTime(new Date());
		cal.set(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DATE), 0, 0, 0);
		return cal.getTime();
	}

	public static Long getWorkingDuration(Date start, Date finish) {
		if (start == null || finish == null) {
			return null;
		}
		Calendar startDate = Calendar.getInstance();
		startDate.setTime(start);
		Calendar endDate = Calendar.getInstance();
		endDate.setTime(finish);
		int startIndex = startDate.get(Calendar.DAY_OF_WEEK);
		int endIndex = endDate.get(Calendar.DAY_OF_WEEK);
		Calendar startSun = Calendar.getInstance();
		startSun.setTime(start);
		Calendar finishSun = Calendar.getInstance();
		finishSun.setTime(finish);
		startSun.add(Calendar.DAY_OF_WEEK, 1 - startIndex);
		finishSun.add(Calendar.DAY_OF_WEEK, 1 - endIndex);
		long count = (((finishSun.getTimeInMillis() - startSun.getTimeInMillis()) / (24 * 3600 * 1000)) / 7) * 5;
		if (endIndex == 7) {
			endIndex = 6;
		}
		if (startIndex != 1) {
			startIndex = startIndex - 1;
		}
		count = count + (endIndex - 1) - (startIndex - 1);
		return count;
	}

	public static Date addDays(Date date, int days) {
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		cal.add(Calendar.DAY_OF_YEAR, days);
		return cal.getTime();
	}

	/***************************************************************************
	 * get Interval between two days, NOTES: It's low efficiency
	 * 
	 * @param start
	 * @param end
	 * @return
	 */
	public static int getDayInterval(Date start, Date end) {
		if (end == null || start == null) {
			return 0;
		}
		final long milliSecondsOfOneDay = 24 * 3600 * 1000;
		return (int) ((roundDate(end).getTime() - roundDate(start).getTime()) / milliSecondsOfOneDay);
	}

}
